package org.example.compare.schedule;

import cn.hutool.core.lang.Pair;
import org.example.compare.dto.OrderDto;
import org.example.compare.dto.OrderProcess;
import org.example.compare.dto.PositionDto;
import org.example.compare.dto.SBoxData;
import org.example.compare.global.SchData;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;

import static org.example.compare.global.Global.*;

@Component
public class PreScheduling {
    @Autowired
    CSchDataCollector cSchDataCollector;

    //计算小时任务数
    boolean calcuteTarget(OrderDto pOrder) {
        long planSec = pOrder.getMiOperate2Deadline();
        long nowSec = planSec - pOrder.getMiNow2DeadlineSecs();
        nowSec += 3600L * gRuleHourTargetWindow;

        //3、构建流程数据

        //4、计算最大筐件数
        int maxCnt = 0;
        int boxCnt = 0;
        for (List<SBoxData> boxes : pOrder.getMCurBoxSetGroupByPart().values()) {
            for(SBoxData box : boxes) {
                maxCnt += box.getMiPlanCnt();
                boxCnt ++;
            }
        }
        if(boxCnt == 0) {
            maxCnt = 5;
        } else {
            maxCnt = (boxCnt + maxCnt - 1) / boxCnt;
        }
        if(maxCnt > pOrder.getMiTotalPlanCnt()) {
            maxCnt = pOrder.getMiTotalPlanCnt();
        }

        float rate = gFeedCyclePercent;
        for (Map.Entry<Long, Map<Integer, OrderProcess>> entry : pOrder.getMOrderProcessSetGroupByPart().entrySet()) {
            double totalTime = 0;
            Map<Integer, OrderProcess> processMap = entry.getValue();
            for (OrderProcess orderProcess : processMap.values()) {
                totalTime += orderProcess.getMfProcessTotalTime();
            }
            double firstBox = totalTime * maxCnt * rate;
            if(firstBox > planSec) {
                firstBox = planSec;
            }
            double leftTime = planSec - firstBox;
            //Map<Long, Pair<Integer, Integer>> proTimePeriod = topSort(processMap.values(), (int)leftTime, maxCnt);
            Map<Long, Pair<Integer, Integer>> proTimePeriod = calcProTimePeriod(processMap.values(), (int)leftTime, maxCnt);
            for (OrderProcess orderProcess : processMap.values()) {
                int planNow = 0;
                Pair<Integer, Integer> period = proTimePeriod.get(orderProcess.getOrderProcessId());
                if(period == null) {
                    orderProcess.getMScheduling().setMPlanHourTarget(0);
                    orderProcess.getMScheduling().setMNowHourTarget(0);
                    continue;
                }
                int startSec = period.getKey(), endSec = period.getValue();
                if(nowSec >= endSec) {
                    planNow = pOrder.getMiTotalPlanCnt();
                } else if(nowSec <= startSec) {
                    planNow = 0;
                } else if(startSec == endSec) {
                    planNow = pOrder.getMiTotalPlanCnt();
                } else {
                    planNow = (int)(pOrder.getMiTotalPlanCnt() * (nowSec - startSec) / (endSec - startSec));
                }
                if(isNormal(orderProcess)) {
                    if(endSec - startSec > 0) {
                        orderProcess.getMScheduling().setMPlanHourTarget(pOrder.getMiTotalPlanCnt() * 3600L / (endSec - startSec));
                    } else {
                        orderProcess.getMScheduling().setMPlanHourTarget(pOrder.getMiTotalPlanCnt());
                    }
                    if(planNow > orderProcess.getMiFinishedCnt()) {
                        if(nowSec <= endSec) {
                            orderProcess.getMScheduling().setMNowHourTarget((planNow - orderProcess.getMiFinishedCnt()) / gRuleHourTargetWindow);
                        } else {
                            long miaoshu = gRuleHourTargetWindow * 3600L - nowSec + endSec;
                            if(miaoshu > 3600) {
                                orderProcess.getMScheduling().setMNowHourTarget((planNow - orderProcess.getMiFinishedCnt()) * 3600L / miaoshu);
                            } else {
                                orderProcess.getMScheduling().setMNowHourTarget(planNow - orderProcess.getMiFinishedCnt());
                            }
                        }
                    } else {
                        if(pOrder.isMbCompletedFlag()) {
                            orderProcess.getMScheduling().setMNowHourTarget(0);
                        } else {
                            orderProcess.getMScheduling().setMNowHourTarget(planNow - orderProcess.getMiFinishedCnt());
                        }
                    }
                } else {
                    orderProcess.getMScheduling().setMPlanHourTarget(0);
                    orderProcess.getMScheduling().setMNowHourTarget(0);
                }
            }
        }

        return false;
    }

    public OrderProcess getProcessWorkingByPosition(long pos) {
        if (!SchData.pPositionDtos.containsKey(pos)) return null;
        PositionDto pPos = SchData.pPositionDtos.get(pos);
        if (pPos.getMiPosStatus() != IN_PRO_ONLY_P_S && pPos.getMiPosStatus() != WAITING_FOR_ONLY_PRO_P_S &&
                pPos.getMiPosStatus() != BUSY_P_S && pPos.getMiPosStatus() != IN_THE_WAY_P_S &&
                pPos.getMBoxInPro().isEmpty() && pPos.getMBoxWaitingForPro().isEmpty()) {
            return null;
        }

        SBoxData pBox = null;
        if (pPos.getMiPosStatus() == IN_PRO_ONLY_P_S) {
            pBox = cSchDataCollector.findBoxByNum(pPos.getMBoxInPro());
            if (pBox == null) return null;
            if (pBox.getMiBoxStatus() != IN_PRO_B_S) return null;
            if (pBox.getMiCurReworkRecordId() > 0) return null;
            if (pBox.isMbRedistributeFlag()) return null;
        } else if (pPos.getMiPosStatus() == WAITING_FOR_ONLY_PRO_P_S) {
            pBox = cSchDataCollector.findBoxByNum(pPos.getMBoxWaitingForPro());
            if (pBox == null) return null;
            if (pBox.getMiBoxStatus() != WAITING_FOR_PRO_B_S) return null;
            if (pBox.getMiCurReworkRecordId() > 0) return null;
            if (pBox.isMbRedistributeFlag()) return null;
        } else if (pPos.getMiPosStatus() == BUSY_P_S) {
            boolean bBuff = false;
            if (!pPos.getMBoxWaitingForPro().isEmpty()) {
                pBox = cSchDataCollector.findBoxByNum(pPos.getMBoxWaitingForPro());
                if (pBox == null) return null;
                if (pBox.getMiBoxStatus() != WAITING_FOR_PRO_B_S) return null;
                if ((pBox.getMiCurReworkRecordId() > 0) || (pBox.isMbRedistributeFlag())) {
                    pBox = null;
                } else {
                    bBuff = true;
                }
            }
            if (!bBuff) {
                pBox = cSchDataCollector.findBoxByNum(pPos.getMBoxInPro());
                if (pBox == null) return null;
                if (pBox.getMiBoxStatus() != IN_PRO_B_S) return null;
                if ((pBox.getMiCurReworkRecordId() > 0) || (pBox.isMbRedistributeFlag())) {
                    return null;
                }
            }
        } else if (pPos.getMiPosStatus() == IN_THE_WAY_P_S) {
            boolean bBuff = false;
            for (List<OrderDto> vOrderOneGroup : SchData.orderGroupByDeadLine) {
                //1.0、循环每个货期里面的订单
                for (OrderDto vOrder : vOrderOneGroup) {
                    //2.0、循环每个部件
                    for (List<SBoxData> pBoxs : vOrder.getMCurBoxSetGroupByPart().values()) {
                        //3.0、循环每个筐
                        for (SBoxData pBoxTemp : pBoxs) {
                            if (pBoxTemp.getMiBoxStatus() == SEND_PRO_B_S) {
                                long iP = Long.parseLong(pBoxTemp.getMStatusInfo());
                                if (SchData.pPositionDtos.containsKey(iP)) {
                                    if (iP == pos) {
                                        pBox = pBoxTemp;
                                        break;
                                    }
                                }
                            }
                        }
                        if (pBox != null) break;
                    }
                    if (pBox != null) break;
                }
                if (pBox != null) break;
            }
            if (pBox != null) {
                if (pBox.getMiBoxStatus() != WAITING_FOR_PRO_B_S) return null;
                if ((pBox.getMiCurReworkRecordId() > 0) || (pBox.isMbRedistributeFlag())) {
                    pBox = null;
                } else {
                    bBuff = true;
                }
            }
            if (!bBuff) {
                if (!pPos.getMBoxWaitingForPro().isEmpty()) {
                    pBox = cSchDataCollector.findBoxByNum(pPos.getMBoxWaitingForPro());
                    if (pBox == null) return null;
                    if (pBox.getMiBoxStatus() != WAITING_FOR_PRO_B_S) return null;
                    if ((pBox.getMiCurReworkRecordId() > 0) || (pBox.isMbRedistributeFlag())) {
                        pBox = null;
                    } else {
                        bBuff = true;
                    }
                }
            }
            if (!bBuff) {
                pBox = cSchDataCollector.findBoxByNum(pPos.getMBoxInPro());
                if (pBox == null) return null;
                if (pBox.getMiBoxStatus() != IN_PRO_B_S) return null;
                if ((pBox.getMiCurReworkRecordId() > 0) || (pBox.isMbRedistributeFlag())) {
                    return null;
                }
            }
        } else if (!pPos.getMBoxWaitingForPro().isEmpty()) {
            pBox = cSchDataCollector.findBoxByNum(pPos.getMBoxWaitingForPro());
            if (pBox == null) return null;
            if (pBox.getMiBoxStatus() != WAITING_FOR_PRO_B_S) return null;
            if (pBox.getMiCurReworkRecordId() > 0) return null;
            if (pBox.isMbRedistributeFlag()) return null;
        } else if (!pPos.getMBoxInPro().isEmpty()) {
            pBox = cSchDataCollector.findBoxByNum(pPos.getMBoxInPro());
            if (pBox == null) return null;
            if (pBox.getMiBoxStatus() != IN_PRO_B_S) return null;
            if (pBox.getMiCurReworkRecordId() > 0) return null;
            if (pBox.isMbRedistributeFlag()) return null;
        }
        if (pBox == null) return null;

        return cSchDataCollector.findProcessByOrderPartSeq(pBox);
    }

    public Set<OrderProcess> findAfterProcess(Collection<OrderProcess> vecPartProcess, OrderProcess pPcur) {
        Set<OrderProcess> directDependOn = new HashSet<>();
        if (null == pPcur) {
            for (OrderProcess pOp : vecPartProcess) {
                if (!SchData.hOrderProcessDependency.containsKey(pOp.getOrderProcessId())) {
                    //没有所依赖的工序的工序，就是进料可做的工序
                    OrderProcess source = new OrderProcess();
                    BeanUtils.copyProperties(pOp, source);
                    directDependOn.add(source);
                }
            }
        } else {
            for (OrderProcess pOp : vecPartProcess) {
                if (SchData.hOrderProcessDependency.containsKey(pOp.getOrderProcessId())) {
                    if (SchData.hOrderProcessDependency.get(pOp.getOrderProcessId()).contains(pPcur.getOrderProcessId())) {
                        OrderProcess source = new OrderProcess();
                        BeanUtils.copyProperties(pOp, source);
                        directDependOn.add(source);
                    }
                }
            }
        }
        return directDependOn;
    }

    //计算每筐完成前置工序所需的时间
    private Map<Long, Pair<Integer, Integer>> calcProTimePeriod(Collection<OrderProcess> processList, int leftTime, int boxCnt) {
        Map<Long, Double> preTime = new HashMap<>();
        Map<Long, Pair<Integer, Integer>> proTimePeriod = new HashMap<>();
        for(OrderProcess orderProcess : processList) {
            Set<OrderProcess> processSet = SchData.orderProcessAllDependency.getOrDefault(orderProcess.getOrderProcessId(), new HashSet<>());
            double time = 0d;
            for(OrderProcess process : processSet) {
                time += process.getMfProcessTotalTime() * boxCnt;
            }
            preTime.put(orderProcess.getOrderProcessId(), time);
        }

        for (Map.Entry<Long, Double> entry : preTime.entrySet()) {
            Long id = entry.getKey();
            int value = entry.getValue().intValue();
            proTimePeriod.put(id, Pair.of(value, value + leftTime));
        }
        return proTimePeriod;
    }

    public boolean isNormal(OrderProcess pProcess) {
        return !isSendOut(pProcess) && !isMidCheck(pProcess) && !isInterrupt(pProcess) && !isOutLine(pProcess) && !isSecondPackup(pProcess) && !isSkip(pProcess);
    }

    public boolean isSkip(OrderProcess pProcess) {
        return pProcess.getMiProcessAttri() == SKIP_PROCESS_ATTRI;
    }

    public boolean isSecondPackup(OrderProcess pProcess) {
        return SECONDPACKUP_PROCESS.equals(pProcess.getMProcessBrief());
    }

    public boolean isOutLine(OrderProcess pProcess) {
        return pProcess.getMiProcessAttri() == OUT_LINE_PROCESS_ATTRI;
    }

    public boolean isInterrupt(OrderProcess pProcess) {
        return INTERRUPT_PROCESS.equals(pProcess.getMProcessBrief());
    }

    public boolean isMidCheck(OrderProcess pProcess) {
        return MID_CHECK_PROCESS.equals(pProcess.getMProcessBrief());
    }

    public boolean isSendOut(OrderProcess pProcess) {
        return SEND_OUT_PROCESS.equals(pProcess.getMProcessBrief());
    }

}
